#include "Demo.h"
#include <windowsx.h>
#include "resource.h"
//////////////////////////////////////////////////////////////////////////
// CDemoApp
//////////////////////////////////////////////////////////////////////////
CDemoApp::CDemoApp()
{

}

CDemoApp::~CDemoApp()
{

}

bool CDemoApp::InitInstance()
{
	m_pMainWnd = new CDemoWnd();
	m_pMainWnd->ShowWindow(SW_SHOW);
																   
	return true;
}

//////////////////////////////////////////////////////////////////////////
// CDemoWnd
//////////////////////////////////////////////////////////////////////////
#define MSGHANDLE(nMsg, MsgProc)  {(nMsg), (&CDemoWnd::MsgProc)}

const CDemoWnd::TMSGMAP CDemoWnd::MsgMapTable[] = {
	MSGHANDLE(WM_CREATE,		OnCreate),
	MSGHANDLE(WM_DESTROY,		OnDestroy), 
	MSGHANDLE(WM_PAINT,			OnPaint),
	MSGHANDLE(WM_LBUTTONDOWN,	OnLButtonDown),
	MSGHANDLE(WM_LBUTTONUP,		OnLButtonUp),
};

CDemoWnd::CDemoWnd(const TCHAR* szAppName)
{
	Create(szAppName, WS_OVERLAPPEDWINDOW, 0, 0, 800, 480, NULL);

	m_bMouseDown = false;
	m_bInRegion	 = false;
	RECT rtBlock = {0, 0, 100, 100};
	m_hRgnBlock = CreateRectRgnIndirect(&rtBlock);
}

CDemoWnd::~CDemoWnd()
{
	DeleteObject(m_hRgnBlock);
}

LRESULT	 CDemoWnd::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	for (int ii = 0; ii < sizeof(MsgMapTable) / sizeof(*MsgMapTable); ii++)
	{
		if (nMsg == MsgMapTable[ii].m_nMsg)
		{
			(this->*MsgMapTable[ii].m_MsgProc)(hWnd, nMsg, wParam, lParam);
			break;
		}
	}

	return __super::FinalWndProc(hWnd, nMsg, wParam, lParam);
}

LRESULT CDemoWnd::OnCreate(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	CenterWindow();

	return 0;
}

LRESULT CDemoWnd::OnDestroy(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	PostQuitMessage(0);

	return 0;
}

LRESULT CDemoWnd::OnPaint(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hDC = BeginPaint(hWnd, &ps);
	
	RECT rtGrids[4];
	PartitionGrid(hDC, rtGrids, 3);

	DemoCreateRgn(hDC, rtGrids[0]);
	
	DemoCombineRgn(hDC, rtGrids[1]);

	DemoRgnOperator(hDC, rtGrids[2]);
	
	EndPaint(hWnd, &ps);

	return 1;
}

LRESULT	CDemoWnd::OnLButtonDown(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	if (PtInRegion(m_hRgnBlock, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))
	{
		m_bMouseDown = true;
		
		RECT rtBlock;
		GetRgnBox(m_hRgnBlock, &rtBlock);
		InvalidateRect(hWnd, &rtBlock, FALSE);
	}

	return 1;
}

LRESULT	CDemoWnd::OnLButtonUp(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	if (PtInRegion(m_hRgnBlock, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)) && m_bMouseDown)
	{		
		m_bInRegion = true;
		m_bMouseDown = false;

		RECT rtBlock;
		GetRgnBox(m_hRgnBlock, &rtBlock);
		InvalidateRect(hWnd, &rtBlock, FALSE);
	};

	return 1;
}

void CDemoWnd::PartitionGrid(HDC hDC, RECT* prtGrids, int nGridCount)
{
	assert(0 < nGridCount);

	//--: Grids rect area
	RECT rtClient;
	GetClientRect(m_hWnd, &rtClient);
	InflateRect(&rtClient, -10, -10);
	
	//==: calculate every grid rect and paint color
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, GetStockObject(DC_BRUSH));
	SetDCBrushColor(hDC, RGB(200, 200, 200));

	const int nGridH	= ((rtClient.bottom - rtClient.top) / nGridCount);
	for (int ii = 0; ii < nGridCount; ii++)
	{
		int nGridTop = rtClient.top + ii * nGridH;
		SetRect(&prtGrids[ii], rtClient.left, nGridTop, rtClient.right, nGridTop + nGridH);
		Rectangle(hDC, prtGrids[ii].left, prtGrids[ii].top, prtGrids[ii].right, prtGrids[ii].bottom);
	}
	SelectObject(hDC, hOldBrush);
	
	return;
}	

void CDemoWnd::DemoCreateRgn(HDC hDC, RECT& rtGrid)
{
	InflateRect(&rtGrid, -5, -20);

	int nSavedDC = SaveDC(hDC);

	LOGBRUSH LogBrush;
	LogBrush.lbColor	= RGB(200, 0, 22);
	LogBrush.lbHatch	= 0;
	LogBrush.lbStyle	= BS_SOLID;
	
	const int nRgnCount = 5;
	assert(0 != nRgnCount);
	const int nX		= rtGrid.left;
	const int nY		= rtGrid.top;
	const int nBlockH	= (rtGrid.bottom - rtGrid.top);
	const int nBlockW	= (rtGrid.right - rtGrid.left) / (nRgnCount + 1);
	const int nInterval = (((rtGrid.right - rtGrid.left) - nBlockW * nRgnCount) / (nRgnCount - 1));

    const TCHAR* szDes[] = { _T("Rectangle"), _T("RoundRect"), _T("Elliptic"), _T("Polygon"), _T("PolyPolygon") };
	SetBkMode(hDC, TRANSPARENT);

	HRGN hRgnList[nRgnCount];
	for (int ii = 0; ii < nRgnCount; ii++)
	{
		LogBrush.lbColor	= RGB((ii * 20) % 255, (ii * 20 * nRgnCount) % 255, (ii * 20 * nRgnCount) % 255);
		HBRUSH hBrush = CreateBrushIndirect(&LogBrush);
		SelectObject(hDC, hBrush);
		int x = nX + (nBlockW + nInterval) * ii;
		switch (ii)
		{
			case 0:
			{
				hRgnList[ii] = CreateRectRgn(x, nY, x + nBlockW, nY + nBlockH);
				PaintRgn(hDC, hRgnList[ii]);
				
				break;
			}
			case 1:
			{
				hRgnList[ii] = CreateRoundRectRgn(x, nY, x + nBlockW, nY + nBlockH, 10, 10);
				FrameRgn(hDC, hRgnList[ii], hBrush, 10, 10);
				break;
			  }
			case 2:
			{
				hRgnList[ii] = CreateEllipticRgn(x, nY, x + nBlockW, nY + nBlockH);
				InvertRgn(hDC, hRgnList[ii]);
				break;
			}
			case 3:
			{
				POINT ptAngle[] = {{x, nY}, {x, nY + nBlockH}, {x + nBlockW, nY + nBlockH}};
				hRgnList[ii] = CreatePolygonRgn(ptAngle, sizeof(ptAngle) / sizeof(*ptAngle), WINDING);
				FillRgn(hDC, hRgnList[ii], hBrush);
				break;
			}
			case 4:
			{
				POINT ptPloys[]	= {
					{x + (nBlockW / 2), nY}, {x + (nBlockW / 3), nY + nBlockH / 2}, {x + (nBlockW / 3) * 2, nY + nBlockH / 2},
					{x + (nBlockW / 3), nY + nBlockH / 2}, {x, nY + nBlockH}, {x + (nBlockW / 2), nY + (nBlockH / 4) * 3},
					{x + (nBlockW / 3) * 2, nY + nBlockH / 2}, {x + (nBlockW / 2), nY + (nBlockH / 4) * 3}, {x + nBlockW, nY + nBlockH}
				};
				int   nPloyCount[]	= {3, 3, 3};
				int   nCount		= 3;
				hRgnList[ii] = CreatePolyPolygonRgn(ptPloys, nPloyCount, nCount, ALTERNATE);
				FillRgn(hDC, hRgnList[ii], hBrush);
				break;
			}	
		}

		if (0 <= ii && ii < sizeof(szDes) / sizeof(*szDes))
		{
			TextOut(hDC, x, rtGrid.bottom + 3, szDes[ii], _tcslen(szDes[ii])); 
		}

		SelectObject(hDC, GetStockObject(DC_BRUSH));
		DeleteObject(hBrush);
	}

	for (int ii = 0; ii < nRgnCount; ii++)
	{
		DeleteObject(hRgnList[ii]);
	}
	
	RestoreDC(hDC, nSavedDC);

	return;
}

void CDemoWnd::DemoCombineRgn(HDC hDC, RECT& rtGrid)
{
	InflateRect(&rtGrid, -10, -15);
	OffsetRect(&rtGrid, 0, -10);

	int nSaveDC = SaveDC(hDC);

	const TCHAR* szDes[] = { _T("SrcShap"), _T("RGN_AND"), _T("RGN_OR"), _T("RGN_XOR"), _T("RGN_DIFF"), _T("RGN_COPY")  };

	HBRUSH hBrush = CreateSolidBrush(RGB(100, 22, 23));
	SetBkMode(hDC, TRANSPARENT);

	LOGBRUSH LogBrush;
	LogBrush.lbColor = RGB(23, 23, 223);
	LogBrush.lbHatch = 0;
	LogBrush.lbStyle = BS_NULL;
	HBRUSH hBrushFrame = CreateBrushIndirect(&LogBrush);
	
	const int nRgnCount = 6;
	assert(0 != nRgnCount);
	const int nX		= rtGrid.left;
	const int nY		= rtGrid.top;
	const int nBlockH	= (rtGrid.bottom - rtGrid.top);
	const int nBlockW	= (rtGrid.right - rtGrid.left) / (nRgnCount + 1);
	const int nInterval = (((rtGrid.right - rtGrid.left) - nBlockW * nRgnCount) / (nRgnCount - 1));

	HRGN hDest	   = CreateRectRgn(10, 10, 22, 22);
	for (int ii = 0; ii < nRgnCount; ii++)
	{
		int x = rtGrid.left + (nBlockW + nInterval) * ii;
	
		HRGN hRgnSrc1  = CreateRectRgn(x, rtGrid.top, x + nBlockW / 2, rtGrid.bottom);
		HRGN hRgnSrc2  = CreateEllipticRgn(x + nBlockW / 4, rtGrid.top, x + nBlockW, rtGrid.bottom);
	
		SelectObject(hDC, hBrushFrame);
		Rectangle(hDC, x, rtGrid.top, x + nBlockW, rtGrid.bottom);

		SelectObject(hDC, hBrush);
		if (0 == ii)
		{
			HBRUSH hBrush1 = CreateSolidBrush(RGB(232, 33, 232));
			FillRgn(hDC, hRgnSrc1, hBrush1);
			HBRUSH hBrush2 = CreateSolidBrush(RGB(0, 233, 222));
			FillRgn(hDC, hRgnSrc2, hBrush2);
			DeleteObject(hBrush1);
			DeleteObject(hBrush2);
		} else {
			CombineRgn(hDest, hRgnSrc1, hRgnSrc2, ii);
			PaintRgn(hDC, hDest);
		}
		if (0 <= ii && ii < sizeof(szDes) / sizeof(*szDes))
		{
			TextOut(hDC, x, rtGrid.bottom + 5, szDes[ii], _tcslen(szDes[ii])); 
		}
		
		SelectObject(hDC, GetStockObject(DC_BRUSH));
		
		DeleteObject(hRgnSrc1);
		DeleteObject(hRgnSrc2);
	}

	FrameRgn(hDC, hDest, hBrush, 10, 10);

	DeleteObject(hBrushFrame);
	DeleteObject(hBrush);
	
	RestoreDC(hDC, nSaveDC);

	return;
}

void CDemoWnd::DemoRgnOperator(HDC hDC, RECT& rtGrid)
{
	int nSaved = SaveDC(hDC);

	HBRUSH hBrush = CreateSolidBrush(RGB(233, 125, 23));

	RECT rtBlock;
	GetRgnBox(m_hRgnBlock, &rtBlock);
	RECT rtDef = {0, 0, 100, 100};
	InflateRect(&rtGrid, -5, -5);

	if (EqualRect(&rtBlock, &rtDef) || rtBlock.right >= rtGrid.right)
	{		
		SetRectRgn(m_hRgnBlock, rtGrid.left, rtGrid.top, rtGrid.left + 100, rtGrid.bottom);
	} else {
		if ((rtBlock.bottom - rtBlock.top) != (rtGrid.bottom - rtGrid.top))
		{
			SetRectRgn(m_hRgnBlock, rtBlock.left, rtGrid.top, rtBlock.left + 100, rtGrid.bottom);
		}
	}

	if (m_bInRegion)
	{
		if (m_bMouseDown)
		{
			InvertRgn(hDC, m_hRgnBlock);
		} else {
			OffsetRgn(m_hRgnBlock, 10, 0);
			FillRgn(hDC, m_hRgnBlock, hBrush);
		}
	} else {
		FillRgn(hDC, m_hRgnBlock, hBrush);
	}
	m_bInRegion = false;

	DeleteObject(hBrush);
	
	RestoreDC(hDC, nSaved);
}